import { Module, VuexModule, getModule, Action, Mutation } from 'vuex-module-decorators'
import store from '@renderer/store'
import { hotModuleUnregisterModule } from '@renderer/utils/helper'
import { generateNewBlockIndexes, generateRadomValue, increaseIndexDimension, initGameData, testMoveDown, testMoveLeft, testMoveRight, testMoveUp } from '@renderer/utils/game'
import _ from 'lodash'
import { BlockIndex, GameSettings, MoveResult, Record } from '@renderer/types/game'

const MODULE_NAME = 'app'

hotModuleUnregisterModule(MODULE_NAME)

@Module({ name: MODULE_NAME, dynamic: true, namespaced: true, store })
export default class AppModule extends VuexModule {
  gridSize = 4 // 游戏格数
  gridData = initGameData(this.gridSize) // 游戏数据
  newBlockNum = 1 // 每次操作后新增方块数
  isGameStart = false // 游戏是否开始
  lastData = [[]] // 上一步数据
  canBack = false // 是否可返回上一步
  power = 0 // 能量值
  lastPower = 0 // 上一步能量值

  get flatGridData () {
    return _.flatten(this.gridData)
  }

  get emptyIndexes () {
    const indexes = []
    this.flatGridData.forEach((item, index) => {
      if (item === 0) indexes.push(index)
    })
    return indexes
  }

  get currentMoves (): {[key: string]: MoveResult} {
    const gridData = this.gridData
    return {
      down: testMoveDown(gridData),
      up: testMoveUp(gridData),
      left: testMoveLeft(gridData),
      right: testMoveRight(gridData)
    }
  }

  get gameScore () {
    return this.flatGridData.reduce((total, value) => {
      return value ? total + Math.pow(2, value - 1) * 2 : total
    }, 0)
  }

  get canUseSkill () {
    return this.power === 100
  }

  @Mutation
  SET_LAST_DATA (data: number[][]) {
    this.lastData = _.cloneDeep(data)
  }

  @Mutation
  SET_CAN_BACK (state: boolean) {
    this.canBack = state
  }

  @Mutation
  SET_GRID_DATA (data: number[][]) {
    this.gridData = _.cloneDeep(data)
  }

  @Mutation
  SET_GRID_ITEM ({ row, col, value }: { row: number, col: number, value: number }) {
    this.gridData[row][col] = value
  }

  @Mutation
  SET_GAME_STATE (state: boolean) {
    this.isGameStart = state
  }

  @Mutation
  SET_GRID_SIZE (size: number) {
    this.gridSize = size
  }

  @Mutation
  SET_NEW_BLOCK_NUM (num: number) {
    this.newBlockNum = num
  }

  @Mutation
  SET_POWER (num: number) {
    this.power = Math.max(0, Math.min(100, num))
  }

  @Mutation
  SET_LAST_POWER (num: number) {
    this.lastPower = Math.max(0, Math.min(100, num))
  }

  @Action
  initGame () {
    this.SET_LAST_DATA([[]])
    this.SET_CAN_BACK(false)
    this.SET_GAME_STATE(true)
    this.generateNewBlock(2)
    this.SET_LAST_POWER(0)
    this.SET_POWER(0)
  }

  @Action
  generateNewBlock (size = this.newBlockNum) {
    const newIndexes = generateNewBlockIndexes(this.emptyIndexes, size)
    newIndexes.forEach(index => {
      const { row, col } = increaseIndexDimension(index, this.gridSize)
      this.SET_GRID_ITEM({ row, col, value: generateRadomValue() })
    })
    this.SET_LAST_POWER(this.power)
    this.SET_POWER(this.power + size)
  }

  @Action
  handleMove (key: string) {
    const moveResult = this.currentMoves[key]
    if (moveResult.canMove) {
      this.SET_LAST_DATA(this.gridData)
      this.SET_CAN_BACK(true)
      this.SET_GRID_DATA(moveResult.result)
      this.generateNewBlock()
    }
  }

  @Action
  handleBack () {
    if (this.canBack) {
      this.SET_GRID_DATA(this.lastData)
      this.SET_CAN_BACK(false)
      this.SET_POWER(this.lastPower)
    }
  }

  @Action
  handleEnd () {
    this.SET_CAN_BACK(false)
    this.SET_GAME_STATE(false)
    this.SET_GRID_DATA(initGameData(this.gridSize))
  }

  @Action
  saveSettings (settings: GameSettings) {
    this.SET_GRID_SIZE(settings.gridSize)
    this.SET_NEW_BLOCK_NUM(settings.newBlockNum)
    this.SET_GRID_DATA(initGameData(settings.gridSize))
  }

  @Action
  initGameFromLoad (record: Record) {
    this.SET_CAN_BACK(false)
    this.SET_GAME_STATE(true)
    this.SET_LAST_DATA([[]])
    this.SET_GRID_DATA(record.data)
    this.SET_GRID_SIZE(record.gridSize)
    this.SET_NEW_BLOCK_NUM(record.newBlockNum)
    this.SET_LAST_POWER(0)
    this.SET_POWER(record.power || 0)
  }

  @Action
  removeBlock (point: BlockIndex) {
    this.SET_LAST_DATA(this.gridData)
    this.SET_LAST_POWER(this.power)
    this.SET_CAN_BACK(true)
    this.SET_GRID_ITEM({ ...point, value: 0 })
    this.SET_POWER(0)
  }

  @Action
  switchBlock (points: BlockIndex[]) {
    this.SET_LAST_DATA(this.gridData)
    this.SET_LAST_POWER(this.power)
    this.SET_CAN_BACK(true)
    const [point1, point2] = points
    const value1 = this.gridData[point1.row][point1.col]
    const value2 = this.gridData[point2.row][point2.col]
    this.SET_GRID_ITEM({ ...point1, value: value2 })
    this.SET_GRID_ITEM({ ...point2, value: value1 })
    this.SET_POWER(0)
  }
}

export const appStore = getModule(AppModule)
